package com.shared_file_bucket.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.shared_file_bucket.common.constant.CommonConstant;
import com.shared_file_bucket.common.domain.R;
import com.shared_file_bucket.common.domain.TreeNode;
import com.shared_file_bucket.common.exception.BusinessException;
import com.shared_file_bucket.common.properties.FsServerProperties;
import com.shared_file_bucket.common.utils.DateUtils;
import com.shared_file_bucket.common.utils.FileExUtils;
import com.shared_file_bucket.common.utils.StringUtils;
import com.shared_file_bucket.model.FileAddDTO;
import com.shared_file_bucket.model.FileVO;
import com.shared_file_bucket.model.ShareDir;
import com.shared_file_bucket.service.LocalFileService;
import com.shared_file_bucket.service.ShareDirService;
import com.shared_file_bucket.service.UserService;
import lombok.AllArgsConstructor;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Service
@AllArgsConstructor
public class LocalFileServiceImpl implements LocalFileService {

    private final FsServerProperties fsServerProperties;

    private final UserService userService;

    private final ShareDirService shareDirService;

    /**
     * 获取站点目录树
     *
     * @param dir
     * @return
     */
    @Override
    public List<TreeNode<String>> getSiteDirectoryTreeData(String root, String dir) {
        List<TreeNode<String>> list = new ArrayList<>();
        // 资源目录
        File siteResourceRoot = new File(root + CommonConstant.DIR_SPLIT + dir);
        if (!siteResourceRoot.exists()) {
            siteResourceRoot.mkdirs();
        }
        list.add(new TreeNode<>(siteResourceRoot.getName(), "", siteResourceRoot.getName(), true));

        list.forEach(node -> loadChildrenDirectories(root, node));
        return list;
    }

    /**
     * 获取指定目录下的文件
     *
     * @param dirPath
     * @param filterFilename
     * @return
     */
    @Override
    public R<List<FileVO>> getSiteFileList(String root, String dirPath, String filterFilename) {

        String path = FileExUtils.normalizePath(root + CommonConstant.DIR_SPLIT + dirPath);
        File file = new File(path);
        if (!file.exists() || !file.isDirectory()) {
            return R.failed("目录参数错误：" + dirPath);
        }
        List<FileVO> list = new ArrayList<>();
        File[] listFiles;

        // 返回指定目录文件
        listFiles = file.listFiles((dir, name) -> {
            if (StringUtils.isNotEmpty(filterFilename)) {
                return name.contains(filterFilename);
            }
            return true;
        });

        if (listFiles != null) {
            for (int i = 0; i < listFiles.length; i++) {
                // 最多显示1000个文件
                File f = listFiles[i];
                String filePath = FileExUtils.normalizePath(f.getAbsolutePath());
                FileVO vo = new FileVO();
                vo.setFilePath(filePath.substring(root.length()));
                vo.setFileName(f.getName());
                vo.setIsDirectory(f.isDirectory());
                vo.setFileSize(f.length());
                vo.setModifyTime(DateUtils.epochMilliToLocalDateTime(f.lastModified()));
                if (f.isFile()) {
                    String fileType = FileExUtils.getExtension(f.getName());
                    // vo.setCanEdit(FileServiceImpl.EDITABLE_FILE_TYPE.contains(fileType));
                }
                vo.setPreview(fsServerProperties.getLocal().getUploadMapping() + vo.getFilePath());
                list.add(vo);
            }
        }
        list.sort((f1, f2) -> {
            if (f2.getIsDirectory() && !f1.getIsDirectory()) {
                return 1;
            } else if (f1.getIsDirectory() && !f2.getIsDirectory()) {
                return -1;
            }
            return f1.getFileName().compareTo(f2.getFileName());
        });
        return R.succeed(list);
    }

    /**
     * 新建文件
     *
     * @param dto
     * @return
     * @throws IOException
     */
    @Override
    public void addFile(String root, FileAddDTO dto) throws IOException {

        String dir = dto.getDir();
        dir = FileExUtils.normalizePath(dir);
        if (dir.startsWith("/")) {
            dir = dir.substring(1);
        }
        if (!dir.endsWith("/")) {
            dir += "/";
        }

        File file = new File(root + CommonConstant.DIR_SPLIT + dir + dto.getFileName());
        if (file.exists()) {
            throw new BusinessException("文件夹已存在");
        }

        if (dto.getIsDirectory()) {
            file.mkdirs();
        } else {
            file.getParentFile().mkdirs();
            FileUtils.writeStringToFile(file, StringUtils.EMPTY, StandardCharsets.UTF_8);
        }
    }

    /**
     * 删除文件
     *
     * @param filePathArr
     * @return
     * @throws IOException
     */
    @Override
    public void deleteFiles(String root, String[] filePathArr) throws IOException {
        for (String filePath : filePathArr) {
            String path = FileExUtils.normalizePath(filePath);
            if (path.startsWith("/")) {
                path = path.substring(1);
            }

            File file = new File(root + CommonConstant.DIR_SPLIT + path);
            if (!file.exists()) {
                return;
            }
            if (file.isDirectory()) {
                FileUtils.deleteDirectory(file);
            } else {
                FileUtils.delete(file);
            }
        }
    }

    /**
     * 上传文件到站点指定目录
     *
     * @param dir
     * @param file
     * @throws IOException
     */
    @Override
    public void uploadFile(String root, String dir, MultipartFile file) throws IOException {
        dir = FileExUtils.normalizePath(dir);
        if (dir.startsWith("/")) {
            dir = dir.substring(1);
        }
        if (!dir.endsWith("/")) {
            dir += "/";
        }

        FileUtils.writeByteArrayToFile(new File(root + CommonConstant.DIR_SPLIT + dir + file.getOriginalFilename()), file.getBytes());
    }

    @Override
    public void renameFile(String root, String filePath, String rename) throws IOException {
        String path = FileExUtils.normalizePath(filePath);
        if (path.startsWith("/")) {
            path = path.substring(1);
        }

        File file = new File(root + CommonConstant.DIR_SPLIT + path);
        if (!file.exists()) {
            throw new BusinessException("文件不存在");
        }

        String dest = root + CommonConstant.DIR_SPLIT + StringUtils.substringBeforeLast(path, "/") + CommonConstant.DIR_SPLIT + rename;
        if (file.isFile()) {
            dest = dest + "." + FileExUtils.getExtension(path);
        }

        File destFile = new File(dest);
        if (destFile.exists()) {
            throw new BusinessException("文件名已存在");
        }

        if (file.isDirectory()) {
            FileUtils.moveDirectory(file, destFile);
        } else {
            FileUtils.moveFile(file, destFile);
        }
    }

    @Override
    public String getRootByUserId() {
        return userService.getById(StpUtil.getLoginIdAsLong()).getRoot();
    }

    @Override
    public String getRootByShareCode(String ShareCode) {
        Long userId = shareDirService.getOne(new LambdaQueryWrapper<ShareDir>().eq(ShareDir::getUuid, ShareCode)).getUserId();
        return userService.getById(userId).getRoot();
    }

    /**
     * 将文件夹打包成zip文件, 并下载
     *
     * @param request
     * @param response
     * @param root
     */
    @Override
    public void packageDirAndDownload(HttpServletRequest request, HttpServletResponse response, String root) throws IOException {
        // 获取文件夹路径对应的Path对象
        Path dirPath = Paths.get(root);

        // 创建ZIP文件输出目录
        String outputDir = "D:/shared-bucket/zip";
        Path outputPath = Paths.get(outputDir);
        Files.createDirectories(outputPath);

        // 创建输出流，并将文件夹打包成ZIP文件
        String zipFileName = "folder.zip";
        FileOutputStream fos = new FileOutputStream(new File(outputPath.toFile(), zipFileName));
        ZipOutputStream zos = new ZipOutputStream(fos);

        // 将文件夹内容写入到响应的输出流中
        Files.walkFileTree(dirPath, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                String zipEntryName = "deded.zip";
                ZipEntry zipEntry = new ZipEntry(zipEntryName);
                zos.putNextEntry(zipEntry);

                FileInputStream fis = new FileInputStream(file.toFile());
                byte[] buffer = new byte[1024];
                int len;
                while ((len = fis.read(buffer)) > 0) {
                    zos.write(buffer, 0, len);
                }

                fis.close();
                return FileVisitResult.CONTINUE;
            }
        });

        // 关闭输出流
        zos.close();
        fos.close();

        // 设置响应的MIME类型为"application/zip"
        response.setContentType("application/zip");

        // 为ZIP文件指定一个文件名
        String fileName = "folder.zip";
        response.setHeader("Content-Disposition", "attachment;filename=" + new java.text.SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(new java.util.Date()) + "_" + fileName);

        // 打印一条消息表示文件夹打包成功
        System.out.println("Folder packaged and downloaded successfully!");
    }


    private void loadChildrenDirectories(String root, TreeNode<String> node) {
        File rootFile = new File(root + CommonConstant.DIR_SPLIT + node.getId());
        File[] listFiles = rootFile.listFiles();
        for (File file : listFiles) {
            if (file.isDirectory()) {
                String id = node.getId() + StringUtils.SLASH + file.getName();
                if (Objects.isNull(node.getChildren())) {
                    node.setChildren(new ArrayList<>());
                }
                TreeNode<String> child = new TreeNode<>(id, node.getId(), file.getName(), false);
                node.getChildren().add(child);
                loadChildrenDirectories(root, child);
            }
        }
    }


}